package com.tiancheng.trade.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tiancheng.trade.commom.core.data.PageInfo;
import com.tiancheng.trade.commom.core.data.Result;
import com.tiancheng.trade.commom.core.data.notify.CallbackSettleMsgBO;
import com.tiancheng.trade.commom.core.data.notify.PayRefundSettleResultMsgBO;
import com.tiancheng.trade.commom.core.data.notify.PaySettleResultMsgBO;
import com.tiancheng.trade.commom.core.enums.*;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import com.tiancheng.trade.commom.web.cache.SettleRulesCache;
import com.tiancheng.trade.commom.web.feign.PaymentFeignService;
import com.tiancheng.trade.commom.web.model.request.RefundVerificationRequestVO;
import com.tiancheng.trade.commom.web.model.response.SettlementRuleResVO;
import com.tiancheng.trade.commom.web.model.response.VerificationOrderResponseVO;
import com.tiancheng.trade.commom.web.utils.ClearingComputeUtils;
import com.tiancheng.trade.order.bo.cmq.RefundDetailResultMsgBO;
import com.tiancheng.trade.order.bo.cmq.RefundResultMsgBO;
import com.tiancheng.trade.order.bo.notify.PayResultOrderBO;
import com.tiancheng.trade.order.bo.notify.PayResultSubOrderBO;
import com.tiancheng.trade.order.convert.BillConvert;
import com.tiancheng.trade.order.mapper.BillMapper;
import com.tiancheng.trade.order.model.*;
import com.tiancheng.trade.order.mq.consumer.CallbackUtil;
import com.tiancheng.trade.order.service.BillDetailService;
import com.tiancheng.trade.order.service.BillService;
import com.tiancheng.trade.order.service.OrderService;
import com.tiancheng.trade.order.util.helper.OrderIdHelper;
import com.tiancheng.trade.order.vo.admin.res.BillQueryResVO;
import com.tiancheng.trade.order.vo.admin.QueryBillReqVO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: kellen
 * @Description:
 * @Date: create in 2024/12/17 15:45
 */
@Slf4j
@Service
public class BillServiceImpl extends ServiceImpl<BillMapper, Bill> implements BillService {

    @Resource
    private BillDetailService billDetailService;
    @Resource
    private OrderIdHelper orderIdHelper;
    @Resource
    private SettleRulesCache settleRulesCache;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private OrderService orderService;
    @Resource
    private PaymentFeignService paymentFeignService;
    @Resource
    private CallbackUtil callbackUtil;


    @Override
    public Boolean createAfterPay(Order order, PayResultOrderBO payResultOrderBO) {
        final List<Bill> bills = this.list(new LambdaQueryWrapper<Bill>().eq(Bill::getOrderId, order.getOrderId())
                .in(Bill::getSubOrderId, payResultOrderBO.getSubOrders().stream().map(PayResultSubOrderBO::getSubOrderId).collect(Collectors.toList())).eq(Bill::getBillType, 0));
        if (CollectionUtils.isNotEmpty(bills)) {
            log.warn("该笔支付已经创建过账单，订单号：{}", order.getOrderId());
            return true;
        }
        BusinessErrorInfoEnum.SYSTEM_ERROR.assertIsTrue(CollectionUtils.isEmpty(bills), "已经成功生成账单！");
        final List<Bill> billList = new ArrayList<>();
        final List<BillDetail> billDetailList = new ArrayList<>();
        for (PayResultSubOrderBO subOrder : payResultOrderBO.getSubOrders()) {
            // 子订单处理
            final Bill bill = new Bill();
            bill.setBillNo(orderIdHelper.getBillNo(subOrder.getSubOrderId()));
            bill.setOrderId(subOrder.getOrderId());
            bill.setSubOrderId(subOrder.getSubOrderId());
            bill.setSubFlowId(subOrder.getSubFlowId());
            bill.setTotalAmount(Objects.nonNull(subOrder.getSubOrderAmount()) ? subOrder.getSubOrderAmount() : 0);
            bill.setPayAmount(Objects.nonNull(subOrder.getPayAmount()) ? subOrder.getPayAmount() : 0);
            bill.setDiscount(Objects.nonNull(subOrder.getDiscount()) ? subOrder.getDiscount() : 0);
            bill.setRefundAmount(0);
            bill.setRefundDiscountAmount(0);
            bill.setSettleStatus(SettleStatusEnum.WAIT.getCode());
            bill.setVerificationStatus(VerificationStatusEnum.WAIT.getCode());
            bill.setBillRemark("");
            bill.setBillType(0);
            bill.setCreatedDt(LocalDateTime.now());
            // 获取清分规则
            final SettlementRuleResVO ruleByMerchantCode = settleRulesCache.getRuleByMerchantCode(order.getOrgPaymentChannelId(), subOrder.getMerchantCode());
            bill.setSettleRule("M" + ruleByMerchantCode.getSettleRuleCode());
            // 明细项
            // 1、实付部分
            final BillDetail detail = new BillDetail();
            detail.setBillNo(bill.getBillNo());
            detail.setFlowId(subOrder.getSubFlowId());
            detail.setChannelId(subOrder.getSubFlowId());
            detail.setBillType(BillTypeEnums.pay_pre.getCode());
            detail.setTotalAmount(Objects.nonNull(subOrder.getPayAmount()) ? subOrder.getPayAmount() : 0);
            detail.setAmount(Objects.nonNull(subOrder.getPayAmount()) ? subOrder.getPayAmount() : 0);
            detail.setIsSettle(1);
            detail.setSettleAmount(Objects.nonNull(subOrder.getPayAmount()) ? subOrder.getPayAmount() : 0);
            detail.setCommissionAmount(ClearingComputeUtils.platformCommission(subOrder.getPayAmount(), ruleByMerchantCode));
            detail.setMerchantAmount(detail.getSettleAmount() - detail.getCommissionAmount());
            detail.setPayStatus("SC");
            detail.setPayWay(payResultOrderBO.getPayWay());
            detail.setCreatedDt(LocalDateTime.now());
            billDetailList.add(detail);
            if (Objects.nonNull(subOrder.getIsCoupon()) && subOrder.getIsCoupon().equals(1) && Objects.nonNull(subOrder.getDiscount()) && subOrder.getDiscount() > 0) {
                // 2、优惠部分
                final BillDetail discountDetail = new BillDetail();
                discountDetail.setBillNo(bill.getBillNo());
                discountDetail.setFlowId(subOrder.getSubFlowId());
                discountDetail.setChannelId(subOrder.getSubFlowId());
                discountDetail.setBillType(BillTypeEnums.pay_channel_discount.getCode());
                discountDetail.setTotalAmount(Objects.nonNull(subOrder.getDiscount()) ? subOrder.getDiscount() : 0);
                discountDetail.setAmount(Objects.nonNull(subOrder.getDiscount()) ? subOrder.getDiscount() : 0);
                discountDetail.setIsSettle(1);
                discountDetail.setSettleAmount(Objects.nonNull(subOrder.getDiscount()) ? subOrder.getDiscount() : 0);
                discountDetail.setCommissionAmount(ClearingComputeUtils.platformCommission(discountDetail.getSettleAmount(), ruleByMerchantCode));
                discountDetail.setMerchantAmount(discountDetail.getSettleAmount() - discountDetail.getCommissionAmount());
                discountDetail.setPayStatus("SC");
                discountDetail.setPayWay(BillTypeEnums.pay_channel_discount.getMsg());
                discountDetail.setCreatedDt(LocalDateTime.now());
                billDetailList.add(discountDetail);
            }

            // 结算规则
            bill.setSubFlowId(subOrder.getSubFlowId());
            bill.setSettleRule(SettleRuleTypeEnums.merchant.getCode() + ruleByMerchantCode.getSettleRuleCode());
            bill.setPlatformPercent(ruleByMerchantCode.getPlatformPre());
            bill.setSettleAmount(billDetailList.stream().mapToInt(BillDetail::getSettleAmount).sum());
            bill.setPlatformAmount(billDetailList.stream().mapToInt(BillDetail::getCommissionAmount).sum());
            bill.setMerchantAmount(bill.getSettleAmount() - bill.getPlatformAmount());
            billList.add(bill);
        }
        return transactionTemplate.execute(transactionStatus -> {
            try {
                saveBatch(billList);
                billDetailService.saveBatch(billDetailList);
                return true;
            } catch (Exception e) {
                transactionStatus.setRollbackOnly();
                throw e;
            }
        });
    }

    @Override
    public Boolean createBillAfterRefund(Order order, RefundResultMsgBO refundResultMsgBO, List<RefundOrdersDetail> refundOrdersDetails) {
        if (!refundResultMsgBO.getStatus().equals("success")) {
            return true;
        }
        final List<String> subOrderIds = refundResultMsgBO.getSubRefunds().stream().map(RefundDetailResultMsgBO::getSubOrderId).collect(Collectors.toList());
        /*final List<Bill> bills = this.list(new LambdaQueryWrapper<Bill>().in(Bill::getSubOrderId, subOrderIds));
        if (CollectionUtils.isEmpty(bills)) {
            return true;
        }*/
        // 已经生成过的退款账单的不处理
        final List<Bill> bills = this.list(new LambdaQueryWrapper<Bill>().in(Bill::getSubOrderId, subOrderIds).eq(Bill::getBillType, 1).in(Bill::getSubFlowId, refundOrdersDetails.stream().map(RefundOrdersDetail::getSubPaymentId).collect(Collectors.toSet())));
        if (CollectionUtils.isNotEmpty(bills) && bills.size() == refundOrdersDetails.size()) {
            log.warn("没有需要再生成的退款账单");
            return true;
        }
        refundOrdersDetails.removeIf(r->bills.stream().anyMatch(b->b.getSubFlowId().equals(r.getSubPaymentId())));
        if(CollectionUtils.isEmpty(refundOrdersDetails)){
            log.warn("没有需要再生成的退款账单");
            return true;
        }
        final List<Bill> billList = new ArrayList<>();
        final List<BillDetail> billDetailList = new ArrayList<>();

        // 根据退款明细
        for (RefundOrdersDetail refundOrdersDetail : refundOrdersDetails) {
            // 子订单处理
            final Bill bill = new Bill();
            bill.setBillNo(orderIdHelper.getBillNo(refundOrdersDetail.getSubPaymentId()));
            bill.setOrderId(refundOrdersDetail.getOrderId());
            bill.setSubOrderId(refundOrdersDetail.getSubOrderId());
            bill.setSubFlowId(refundOrdersDetail.getSubPaymentId());
            bill.setTotalAmount(0);
            bill.setPayAmount(0);
            bill.setDiscount(0);
            bill.setRefundAmount(refundOrdersDetail.getRefundAmount());
            bill.setRefundDiscountAmount(refundOrdersDetail.getDiscount());
            bill.setSettleStatus(SettleStatusEnum.WAIT.getCode());
            bill.setVerificationStatus(VerificationStatusEnum.WAIT.getCode());
            bill.setBillRemark("");
            bill.setBillType(1);
            bill.setCreatedDt(LocalDateTime.now());
            // 获取清分规则
            final SettlementRuleResVO ruleByMerchantCode = settleRulesCache.getRuleByMerchantCode(order.getOrgPaymentChannelId(), refundOrdersDetail.getMerchantCode());
            bill.setSettleRule("M" + ruleByMerchantCode.getSettleRuleCode());

            // 1、实付部分
            final BillDetail detail = new BillDetail();
            detail.setBillNo(bill.getBillNo());
            detail.setFlowId(refundOrdersDetail.getSubPaymentId());
            detail.setChannelId(refundOrdersDetail.getSubPaymentId());
            detail.setBillType(BillTypeEnums.refund_pre.getCode());
            detail.setTotalAmount(refundOrdersDetail.getRefundApplyAmount());
            detail.setAmount(refundOrdersDetail.getRefundAmount());
            detail.setIsSettle(1);
            detail.setSettleAmount(negativeAmount(refundOrdersDetail.getRefundApplyAmount()));
            detail.setCommissionAmount(negativeAmount(ClearingComputeUtils.platformCommission(refundOrdersDetail.getRefundApplyAmount(), ruleByMerchantCode)));
            detail.setMerchantAmount(detail.getSettleAmount() - detail.getCommissionAmount());
            detail.setPayStatus("SC");
            detail.setPayWay(order.getPayWay());
            detail.setCreatedDt(LocalDateTime.now());
            billDetailList.add(detail);
            if (!refundOrdersDetail.getRefundAmount().equals(refundOrdersDetail.getRefundApplyAmount())) {
                // 2、优惠部分
                final BillDetail discountDetail = new BillDetail();
                discountDetail.setBillNo(bill.getBillNo());
                discountDetail.setFlowId(refundOrdersDetail.getSubPaymentId());
                discountDetail.setChannelId(refundOrdersDetail.getSubPaymentId());
                discountDetail.setBillType(BillTypeEnums.refund_channel_discount.getCode());

                discountDetail.setTotalAmount(refundOrdersDetail.getDiscount());
                discountDetail.setAmount(refundOrdersDetail.getRefundApplyAmount() - refundOrdersDetail.getRefundAmount());
                discountDetail.setIsSettle(1);
                discountDetail.setSettleAmount(negativeAmount(discountDetail.getAmount()));
                discountDetail.setCommissionAmount(negativeAmount(ClearingComputeUtils.platformCommission(discountDetail.getAmount(), ruleByMerchantCode)));
                discountDetail.setMerchantAmount(discountDetail.getSettleAmount() + discountDetail.getCommissionAmount());
                discountDetail.setPayStatus("SC");
                discountDetail.setPayWay(BillTypeEnums.refund_channel_discount.getMsg());
                discountDetail.setCreatedDt(LocalDateTime.now());
                billDetailList.add(discountDetail);
            }

            // 结算规则
            bill.setRefundTotalAmount(billDetailList.stream().mapToInt(BillDetail::getTotalAmount).sum());
            bill.setRefundAmount(billDetailList.stream().mapToInt(BillDetail::getAmount).sum());
            bill.setRefundDiscountAmount(bill.getRefundTotalAmount() - bill.getRefundAmount());
            bill.setSettleRule(SettleRuleTypeEnums.merchant.getCode() + ruleByMerchantCode.getSettleRuleCode());
            bill.setPlatformPercent(ruleByMerchantCode.getPlatformPre());
            bill.setSettleAmount(billDetailList.stream().mapToInt(BillDetail::getSettleAmount).sum());
            bill.setPlatformAmount(billDetailList.stream().mapToInt(BillDetail::getCommissionAmount).sum());
            bill.setMerchantAmount(bill.getSettleAmount() - bill.getPlatformAmount());
            billList.add(bill);
        }
        return transactionTemplate.execute(transactionStatus -> {
            try {
                saveBatch(billList);
                billDetailService.saveBatch(billDetailList);
                return true;
            } catch (Exception e) {
                transactionStatus.setRollbackOnly();
                throw e;
            }
        });
    }

    /**
     * 支付账单完结
     *
     * @param params 支付参数
     * @return
     */
    @Override
    public Boolean paymentSettleResult(PaySettleResultMsgBO params) {
        // 查询核销id+支付子流水号对应的支付账单
        final List<Bill> bills = this.list(new LambdaQueryWrapper<Bill>().eq(Bill::getVerificationId, params.getVerificationId()).eq(Bill::getSubFlowId, params.getSubFlowId()).eq(Bill::getBillType, 0));
        if (CollectionUtils.isEmpty(bills)) {
            log.info("未查询到支付账单，核销id：{}", params.getVerificationId());
            return true;
        }
        if (bills.stream().allMatch(b -> b.getSettleStatus().equals(SettleStatusEnum.SUCCESS.getCode()))) {
            return true;
        }

        CallbackSettleMsgBO callbackSettleMsgBO = BillConvert.INSTANCE.bill2CallbackPaySettleMsgBO(bills.get(0));
        // 账单结算状态处理
        Bill bill = new Bill();
        bill.setSettleStatus(SettleStatusEnum.SUCCESS.getCode());
        bill.setSettleDate(params.getSettleDate());
        bill.setSettleScDate(null != params.getSettleScDate() ? params.getSettleScDate().toLocalDate() : LocalDate.now());
        callbackSettleMsgBO.setSettleStatus(bill.getSettleStatus());
        callbackSettleMsgBO.setSettleScDate(bill.getSettleScDate());
        callbackSettleMsgBO.setSettleDate(bill.getSettleDate());
        if (null != params.getMerchantAmount()) {
            bill.setMerchantAmount(params.getMerchantAmount());
            callbackSettleMsgBO.setMerchantAmount(params.getMerchantAmount());
        }
        if (null != params.getPlatformAmount()) {
            bill.setPlatformAmount(params.getPlatformAmount());
            callbackSettleMsgBO.setPlatformAmount(params.getPlatformAmount());
        }

        bill.setLastUpdDt(LocalDateTime.now());
        this.update(bill, new LambdaQueryWrapper<Bill>().eq(Bill::getId, bills.get(0).getId()));

        Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderId, bills.get(0).getOrderId()));
        // TODO 回调业务系统？
        if (StringUtils.isEmpty(order.getNotifyUrl())) {
            log.info("回调地址为空，不回调业务系统！");
            return true;
        }
        callbackSettleMsgBO.setPaymentChannel(order.getPayChannel());
        callbackUtil.callTarget(order.getNotifyUrl(), MessageTypeEnum.trade_payment_settle_result, callbackSettleMsgBO);
        return true;
    }

    @Override
    public Boolean refundSettleResult(PayRefundSettleResultMsgBO params) {
        // 查询核销id+支付子流水号对应的支付账单
        final List<Bill> bills = this.list(new LambdaQueryWrapper<Bill>().eq(Bill::getVerificationId, params.getVerificationId()).eq(Bill::getSubFlowId, params.getSubPaymentId()).eq(Bill::getBillType, 1));
        if (CollectionUtils.isEmpty(bills)) {
            log.info("未查询到退款账单，核销id：{}", params.getVerificationId());
            return true;
        }
        if (bills.stream().allMatch(b -> b.getSettleStatus().equals(SettleStatusEnum.SUCCESS.getCode()))) {
            return true;
        }
        CallbackSettleMsgBO callbackSettleMsgBO = BillConvert.INSTANCE.bill2CallbackPaySettleMsgBO(bills.get(0));
        // 账单结算状态处理'
        Bill bill = new Bill();
        bill.setSettleDate(params.getSettleDate());
        bill.setSettleStatus(SettleStatusEnum.SUCCESS.getCode());
        bill.setSettleScDate(params.getSettleScDate());
        callbackSettleMsgBO.setSettleStatus(bill.getSettleStatus());
        callbackSettleMsgBO.setSettleScDate(bill.getSettleScDate());
        callbackSettleMsgBO.setSettleDate(bill.getSettleDate());
        if (null != params.getMerchantAmount()) {
            bill.setMerchantAmount(params.getMerchantAmount());
            callbackSettleMsgBO.setMerchantAmount(params.getMerchantAmount());
        }
        if (null != params.getPlatformAmount()) {
            bill.setPlatformAmount(params.getPlatformAmount());
            callbackSettleMsgBO.setPlatformAmount(params.getPlatformAmount());
        }
        bill.setLastUpdDt(LocalDateTime.now());
        this.update(bill, new LambdaQueryWrapper<Bill>().in(Bill::getId, bills.stream().map(Bill::getId).toList()));

        Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderId, bills.get(0).getOrderId()));
        // TODO 回调业务系统？
        if (StringUtils.isEmpty(order.getNotifyUrl())) {
            log.info("回调地址为空，不回调业务系统！");
            return true;
        }
        callbackSettleMsgBO.setPaymentChannel(order.getPayChannel());
        callbackUtil.callTarget(order.getNotifyUrl(), MessageTypeEnum.trade_payment_refund_settle_result, callbackSettleMsgBO);
        return true;
    }

    @Override
    public void refundVerification(List<RefundOrdersDetail> refundOrdersDetails) {
        // 查询退款账单
        List<Bill> bills = this.list(new LambdaQueryWrapper<Bill>().eq(Bill::getBillType, 1).in(Bill::getSubFlowId, refundOrdersDetails.stream().map(RefundOrdersDetail::getSubPaymentId).toList())
                .in(Bill::getVerificationStatus, Arrays.asList(VerificationStatusEnum.WAIT.getCode(), VerificationStatusEnum.FL.getCode())));
        if (CollectionUtils.isEmpty(bills)) {
            log.info("退款核销，未查询到退款账单！");
            return;
        }

        RefundVerificationRequestVO refundVerificationRequestVO = new RefundVerificationRequestVO();
        refundVerificationRequestVO.setFlowId(refundOrdersDetails.get(0).getFlowId());
        refundVerificationRequestVO.setVerificationId(orderIdHelper.getVerificationId(refundOrdersDetails.get(0).getSubPaymentId()));
        Result<VerificationOrderResponseVO> verificationResult = paymentFeignService.refundVerification(refundVerificationRequestVO);
        if (verificationResult.getRet() == 0) {
            Bill bill = new Bill();
            bill.setVerificationStatus(VerificationStatusEnum.SC.getCode());
            bill.setVerificationTime(LocalDateTime.now());
            bill.setVerificationId(refundVerificationRequestVO.getVerificationId());
            bill.setSettleStatus(SettleStatusEnum.WAIT.getCode());
            this.update(bill, new LambdaQueryWrapper<Bill>().in(Bill::getId, bills.stream().map(Bill::getId).toList()));
        }
    }

    @Override
    public PageInfo<BillQueryResVO> queryPage(QueryBillReqVO params) {
        IPage<Bill> page = new Page<>(params.getCurrentPage(), params.getPageSize());
        if (null != params.getBillDateBegin()) {
            params.setBillDateBegin(LocalDateTime.of(LocalDate.from(params.getBillDateBegin()), LocalDateTime.MIN.toLocalTime()));
        }
        if (null != params.getBillDateEnd()) {
            params.setBillDateEnd(LocalDateTime.of(LocalDate.from(params.getBillDateEnd()), LocalDateTime.MAX.toLocalTime()));
        }
        final IPage<BillQueryResVO> pageResult = this.baseMapper.queryPage(page, params);
        return new PageInfo<>(pageResult.getRecords(),new PageInfo.Pagination(pageResult.getCurrent(), (int) pageResult.getSize(), pageResult.getTotal()));
    }


    private Integer negativeAmount(Integer sourceAmount) {
        return null == sourceAmount || sourceAmount == 0 ? 0 : -sourceAmount;
    }
}
